using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Jemgine.Hex;

namespace HexDemo
{
    public class Main : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        DepthStencilState DepthState;

        OrthographicCamera Camera = new OrthographicCamera(new Vector2(800, 480));
        protected ImmediateMode Renderer;

        protected Vector2 DrawOffset;

        Texture2D texHorizontalHex;
        Texture2D texVerticalHex;

        protected Coordinate SelectedHex = new Coordinate(2, 3);

        private SpriteFont debug_font;
        private String debug_string;

        class Level
        {
            internal CoordinateSystem CoordinateSystem;
            internal HexVertexBuffers VertexBuffers;

            internal Level Next = null;

            internal static Level Build(
                CoordinateSystem Parent,
                int Depth, 
                SubdivisionTechniques Technique,
                GraphicsDevice GraphicsDevice)
            {
                Level Result = new Level();
                Result.CoordinateSystem = Parent.CreateSubSpace(Technique);
                Result.VertexBuffers = new HexVertexBuffers();
                DrawHex.PrepareVertexBuffers(Result.VertexBuffers, Result.CoordinateSystem, GraphicsDevice);
                if (Depth > 1) Result.Next = Level.Build(Result.CoordinateSystem, Depth - 1, Technique, GraphicsDevice);
                return Result;
            }

            internal static Level Build(
                double Radius,
                int Depth,
                SubdivisionTechniques Technique,
                GraphicsDevice GraphicsDevice,
                Orientation Orientation)
            {
                Level Result = new Level();
                if (Orientation == Orientation.Horizontal)
                    Result.CoordinateSystem = new HorizontalCoordinateSystem(Radius);
                else
                    Result.CoordinateSystem = new VerticalCoordinateSystem(Radius);
                Result.VertexBuffers = new HexVertexBuffers();
                DrawHex.PrepareVertexBuffers(Result.VertexBuffers, Result.CoordinateSystem, GraphicsDevice);
                if (Depth > 1) Result.Next = 
                    Level.Build(Result.CoordinateSystem, Depth - 1, Technique, GraphicsDevice);
                return Result;
            }

            internal void Draw(
                ImmediateMode Renderer, 
                Texture2D texVerticalHex, 
                Texture2D texHorizontalHex, 
                GraphicsDevice GraphicsDevice)
            {
                Renderer.PushMatrix(Matrix.CreateTranslation(new Vector3(CoordinateSystem.GetSuperOffset(), 0)));

                for (int X = (int)(-(800.0 / CoordinateSystem.GetRadius())); X < (int)((800.0 / CoordinateSystem.GetRadius())); ++X)
                    for (int Y = (int)(-(480.0 / CoordinateSystem.GetRadius())); Y < (int)((480.0 / CoordinateSystem.GetRadius())); ++Y)
                        DrawHex.DrawTexturedHex(VertexBuffers, 
                            CoordinateSystem.GetOrientation() == Orientation.Horizontal ?
                            texHorizontalHex : texVerticalHex,
                            Renderer,
                            CoordinateSystem.Center(new Coordinate(X, Y)),
                            GraphicsDevice);

                if (Next != null) Next.Draw(Renderer, texVerticalHex, texHorizontalHex, GraphicsDevice);

                Renderer.PopMatrix();
            }
        }

        Level TopLevel;

        public Main()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            this.TargetElapsedTime = TimeSpan.FromSeconds(1.0 / 60.0);
            graphics.PreferredBackBufferWidth = 800;
            graphics.PreferredBackBufferHeight = 480;
            graphics.IsFullScreen = false;

            IsMouseVisible = true;
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            TopLevel = Level.Build(128.0, 2, SubdivisionTechniques.AlternateOrientation, GraphicsDevice, Orientation.Vertical);

            Camera.offset = Vector2.Zero;

            DrawOffset.X = -400;
            DrawOffset.Y = -240;

            Renderer = new ImmediateMode(GraphicsDevice);
            texHorizontalHex = Content.Load<Texture2D>("horizontal_hex");
            texVerticalHex = Content.Load<Texture2D>("vertical_hex");

            DepthState = new DepthStencilState();
            DepthState.DepthBufferEnable = true;
            DepthState.DepthBufferWriteEnable = true;
            DepthState.DepthBufferFunction = CompareFunction.LessEqual;

            debug_font = Content.Load<SpriteFont>("debugfont");
        }

        protected override void UnloadContent()
        {
        }

        KeyboardState PreviousKeyState = new KeyboardState();

        bool KeyPressed(KeyboardState Previous, KeyboardState New, Keys Key)
        {
            if (Previous.IsKeyUp(Key) && New.IsKeyDown(Key)) return true;
            return false;
        }

        protected override void Update(GameTime gameTime)
        {
            var MouseState = Mouse.GetState();
            Vector2 MousePos = TransformToWorldSpace(new Vector2(MouseState.X, MouseState.Y));

            debug_string = "";

            debug_string += MousePos.ToString();

            Level _lvl = TopLevel;
            while (_lvl != null)
            {
                MousePos -= _lvl.CoordinateSystem.GetSuperOffset();
                debug_string += _lvl.CoordinateSystem.TileAt(MousePos).ToString();
                _lvl = _lvl.Next;
            }

            var KeyState = Keyboard.GetState();

            if (KeyPressed(PreviousKeyState, KeyState, Keys.D1)) 
                TopLevel = Level.Build(48.0, 2, SubdivisionTechniques.AlternateOrientation, GraphicsDevice, Orientation.Horizontal);
            if (KeyPressed(PreviousKeyState, KeyState, Keys.D2))
                TopLevel = Level.Build(48.0, 2, SubdivisionTechniques.DivideByThree, GraphicsDevice, Orientation.Vertical);
            if (KeyPressed(PreviousKeyState, KeyState, Keys.D3))
                TopLevel = Level.Build(48.0, 2, SubdivisionTechniques.DivideByThree, GraphicsDevice, Orientation.Horizontal);
            if (KeyPressed(PreviousKeyState, KeyState, Keys.D4))
                TopLevel = Level.Build(48.0, 2, SubdivisionTechniques.AlternateOrientation, GraphicsDevice, Orientation.Vertical);

            PreviousKeyState = KeyState;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.DepthStencilState = DepthState;
            GraphicsDevice.BlendState = BlendState.AlphaBlend;

            Renderer.SetCamera(Camera);
            Renderer.PushMatrix(Matrix.CreateTranslation(DrawOffset.X, DrawOffset.Y, 0));

            TopLevel.Draw(Renderer, texVerticalHex, texHorizontalHex, GraphicsDevice);

            Renderer.PopMatrix();

            spriteBatch.Begin();
            spriteBatch.DrawString(debug_font, debug_string, Vector2.Zero, Color.Red);
            spriteBatch.End();
            base.Draw(gameTime);
        }

        public Vector2 TransformToWorldSpace(Vector2 vec)
        {
            vec.Y *= -1;
            vec += new Vector2(-400, 240);
            vec -= DrawOffset;
            return vec;
        }
    }
}
